package com.maple.retry.retrytask;

import com.maple.retry.retrytask.util.TimeUtil;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * @author: maple
 * @version: ScheduleStrategy.java, v 0.1 2020年12月01日 19:40 maple Exp $
 */
public class ScheduleStrategy {

    /**
     * 默认间隔时间
     */
    private static final String DEFAULT_INTERVAL = "10m";

    /**
     * 重发间隔，以毫秒为单位
     * <p>
     * 默认重试一次，重试间隔为10m
     */
    private long[] retryIntervals = new long[]{10 * 60 * 1000};

    /**
     * 字符串类型重试间隔
     * <p>
     * 默认重试一次，重试间隔为10m
     */
    private String strRetryIntervals = "time:" + DEFAULT_INTERVAL;

    /**
     * 是否一直执行
     */
    private boolean forever = false;

    /**
     * 默认永久执行的间隔时间
     */
    private String intervalConstant = DEFAULT_INTERVAL;

    /**
     * 默认永久执行的间隔时长
     */
    private long foreverRetryInterval = 10 * 60 * 1000L;

    public ScheduleStrategy(String strRetryIntervals) {

        this.strRetryIntervals = strRetryIntervals;
        this.retryIntervals = this.parseInterval(strRetryIntervals);
    }

    /**
     * 重置配置
     *
     * @param strRetryIntervals 字符串类型的时间间隔配置
     */
    public void reset(String strRetryIntervals) {
        this.strRetryIntervals = strRetryIntervals;
        this.retryIntervals = this.parseInterval(strRetryIntervals);
    }

    /**
     * 是否永远重试
     *
     * @return 是否需要一直重试
     */
    public boolean isForever() {
        return this.forever;
    }

    /**
     * 重发间隔
     *
     * @return Returns the retryIntervals.
     */
    public long[] getLongRetryIntervals() {
        return retryIntervals;
    }

    public String getStrRetryIntervals() {
        return strRetryIntervals;
    }

    /**
     * 下一次任务调度延迟时间
     *
     * @param retryNo 重试次数
     * @return 延迟时间
     */
    public long nextDelayTime(int retryNo) {

        if (forever && retryNo >= getLongRetryIntervals().length) {
            return foreverRetryInterval;
        }

        return getLongRetryIntervals()[retryNo];
    }

    /**
     * Parse string of time interval.
     * <p>
     * Format:
     * <pre>
     *  time:[Number][Unit],[Number][Unit]...[Number][Unit]
     * </pre>
     * <ul>
     * <li>[Number]: 0 and positive integer, default to 0 if illegal
     * <li>[Unit]: possible value: y(year), M(month), w(week), d(day), h(hour), m(minute), s(second) . default <i>s</i>
     * </ul>
     * <p>
     * time:2s~20s
     * time:2s
     *
     * @param originalIntervalStr string of time interval
     * @return sequence of time in millisecond . Not <tt>null</tt>.
     */
    public long[] parseInterval(String originalIntervalStr) {

        if (StringUtils.isBlank(originalIntervalStr) || !originalIntervalStr.startsWith("time:")) {
            throw new RuntimeException("非法的重试策略配置: " + originalIntervalStr);
        }

        originalIntervalStr = originalIntervalStr.trim();

        int index = originalIntervalStr.indexOf(TimeUtil.COLON);

        try {
            originalIntervalStr = originalIntervalStr.substring(index + 1).trim();
        } catch (Exception e) {
            originalIntervalStr = "";
        }

        this.forever = originalIntervalStr.contains(TimeUtil.TILDE);

        String intervalStr = originalIntervalStr;

        if (forever) {

            try {
                intervalStr = originalIntervalStr.split(TimeUtil.TILDE)[0];
                intervalConstant = originalIntervalStr.split(TimeUtil.TILDE)[1];
                String foreverTimeStr = intervalConstant.substring(0, intervalConstant.length() - 1);
                String foreverUnit = intervalConstant.substring(intervalConstant.length() - 1);
                foreverRetryInterval = TimeUtil.parseTime(foreverTimeStr, foreverUnit);

            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        String[] intervals = intervalStr.split(TimeUtil.COMMA);

        List<Long> intervalNums = new ArrayList<Long>();

        for (String interval : intervals) {

            if (StringUtils.isNotBlank(interval)) {

                interval = interval.trim();

                String timeStr = interval.substring(0, interval.length() - 1);

                String unit = interval.substring(interval.length() - 1);

                validTimeConfig(timeStr, unit);

                intervalNums.add(TimeUtil.parseTime(timeStr, unit));

            } else {

                intervalNums.add(0L);
            }
        }

        long[] result = new long[intervalNums.size()];

        for (int i = 0; i < intervalNums.size(); i++) {
            result[i] = intervalNums.get(i).longValue();
        }

        return result;
    }

    public void validTimeConfig(String timeStr, String timeUnit) {

        try {

            Integer.valueOf(timeStr);

        } catch (Throwable t) {
            throw new RuntimeException("illegal time config: " + timeStr);
        }

        if (!TimeUtil.validTimeUnit.contains(timeUnit)) {
            throw new RuntimeException();
        }
    }

}
